package qc.module.iot.protocol.custom;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qc.common.core.enums.iot.IotPacketsDataFrameTypeEnum;
import qc.common.core.enums.iot.IotPacketsDataProtocolEnum;
import qc.common.core.utils.LocalDateTimeUtil;
import qc.common.core.utils.RadixConvertUtil;
import qc.module.iot.protocol.*;
import qc.module.iot.protocol.sl651.SL651_Constant;
import qc.module.iot.protocol.sl651.SL651_Hex_FrameParse;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * 德宏水文分局发送至龙江水情系统数据帧解析类
 *
 * @author QuCheng Tech
 * @since 2025/11/16
 */
public class Custom_DeHongSwj_FrameParse extends IotFrameParseBase {
    private static final Logger logger = LoggerFactory.getLogger(Custom_DeHongSwj_FrameParse.class);

    /**
     * 数据帧报文协议
     */
    private static final IotPacketsDataProtocolEnum protocol = IotPacketsDataProtocolEnum.CUSTOM_DEHONG_SWJ;

    /**
     * 解析数据帧错误提示信息前缀
     */
    private static final String parseErrorMessagePrefix = "使用定制协议[德宏水文分局发送至龙江水情系统数据协议]解析数据帧时错误，";

    /**
     * 数据帧最小长度，包含起始符和结束符的最小有效长度
     */
    private static final int Frame_Length_Min_Total = 2;

    /**
     * 数据帧起始符字节数组，自定义心跳数据，为2个英文字母XT
     */
    private static final byte[] Frame_Start_Bytes_Heart_Beat = new byte[]{(byte) 'X', (byte) 'T'};
    /**
     * 数据帧起始符字节数组，使用SL651 Hex协议
     */
    private static final byte[] Frame_Start_Bytes_SL651_Ascii = SL651_Constant.SOH_HEX;
    /**
     * 数据帧起始符字节数组，自定义雨量数据，使用2个字节0xBE开始
     */
    private static final int[] Frame_Start_Bytes_Custom1 = new int[]{0xBE, 0xBE};

    /**
     * 数据帧长度，自定义余量数据，使用2个字节0xBE开始的报文总长度
     */
    private static final int Frame_Length_Custom1 = 39;

    /**
     * 监测参数无数据或未采集该参数值时的所有字节取值
     */
    private static final byte NO_VALUE_Byte = (byte) 0xFF;

    /**
     * SL651 Hex协议解析类
     */
    private SL651_Hex_FrameParse sl651HexFrameParse = null;

    public Custom_DeHongSwj_FrameParse() {
        //通过调用父类的构造函数传入协议中的固定内容
        super(protocol, null, null, Frame_Length_Min_Total);
        sl651HexFrameParse = new SL651_Hex_FrameParse();
        //也可以在调用构造函数后显式对变量或调用方法进行赋值
        //this
    }

    /**
     * 分解报文，由于有不同的起始符需要重载父类中的分解报文方法
     *
     * @param packetsBytes 报文字节数组
     * @return java.util.List<qc.module.iot.protocol.IotPacketsSplitResult>
     * @author QuCheng Tech
     * @since 2025/11/17
     */
    @Override
    public List<IotPacketsSplitResult> split(byte[] packetsBytes) {
        logger.debug("split");
        if (packetsBytes == null || packetsBytes.length < 0x1) {
            //如果传入的报文为空无法进行分解
            logger.debug("分解报文数据帧时报文内容为空");
        } else {
            if (this.frameMinLength > 0x0 && packetsBytes.length < this.frameMinLength) {
                //如果有传入最小数据帧长度，判断报文总长要≥最小数据帧长度
                logger.debug("分解报文数据帧时报文内容长度不够，报文总长度[{}]＜数据帧最小长度[{}]", packetsBytes.length, this.frameMinLength);
            } else {
                List<IotPacketsSplitResult> result = new ArrayList<>();

                //遍历传输的报文，从第0个开始，至报文总长度
                int index = 0x0;
                while (index < packetsBytes.length) {
                    /* 德宏水文分局发送至龙江水情系统数据
                     * 自定义协议，根据起始的1-2个字节不同有不同的格式
                     * 第1种：心跳报文，起始的2字节为英文字母XT，无其他内容
                     * 第2种：数据报文，使用SL651 Hex协议，起始的2字节为7E7E，完整使用SL651 Hex协议进行解析
                     * 第3种：数据报文，自定义协议，起始的2字节为BEBE
                     *
                     * 第3种：数据报文，自定义协议，起始的2字节为BEBE
                     * 结构框架：帧起始符 遥测站站号 时间 报文正文
                     * 长度字节：   2       4     5     2*13
                     * 固定内容： 帧起始符-BEBE，报文总长度固定是39字节（包含帧起始符的2个BE）
                     * 解析举例：BE BE 40 00 00 51 01 01 25 04 14 14 00 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17
                     * 类型：【遥测站小时报】，数据类型：【雨量】，站号【40000051】，时间【2025-04-14 14:00:00】，值【558.5】
                     * 时间格式：yyMMddHHmm，数值为对应的字符串为十进制的值*0.5分辨率 1117*0.5=558.5
                     *
                     */

                    if (packetsBytes[index] == this.Frame_Start_Bytes_Heart_Beat[0x0] && packetsBytes[index + 1] == this.Frame_Start_Bytes_Heart_Beat[0x1]) {
                        //匹配第1种：心跳报文，起始的2字节为英文字母XT，无其他内容
                        IotPacketsSplitResult splitPackets = new IotPacketsSplitResult();
                        splitPackets.setStartIndex(index);
                        splitPackets.setEndIndex(index + 1);
                        byte[] bytes = Arrays.copyOfRange(packetsBytes, index, index + 0x2);//copyOfRange方法中to的位置索引是不包含的，因此需要加1
                        splitPackets.setBytes(bytes);

                        result.add(splitPackets);
                        index += 0x2;
                    } else if (packetsBytes[index] == this.Frame_Start_Bytes_SL651_Ascii[0x0] && packetsBytes[index + 1] == this.Frame_Start_Bytes_SL651_Ascii[0x1]) {
                        //匹配第2种：数据报文，使用SL651 Hex协议，起始的2字节为7E7E
                        byte[] sl651Bytes = Arrays.copyOfRange(packetsBytes, index, packetsBytes.length);
                        //调用SL651 Hex协议解析类中的分解方法
                        List<IotPacketsSplitResult> sl651SplitResult = sl651HexFrameParse.split(sl651Bytes);
                        if (sl651SplitResult != null && sl651SplitResult.size() > 0x0) {
                            int sl651StartIndex = 0x0;
                            for (IotPacketsSplitResult splitResult : sl651SplitResult) {
                                sl651StartIndex = splitResult.getStartIndex();
                                IotPacketsSplitResult splitPackets = new IotPacketsSplitResult();
                                splitPackets.setStartIndex(index + sl651StartIndex);
                                splitPackets.setEndIndex(index + splitResult.getEndIndex());
                                splitPackets.setBytes(splitResult.getBytes());

                                result.add(splitPackets);
                            }
                            //如果有分解出SL651协议报文，下个字节从最后一个SL651协议报文开始后2字节开始
                            index += (sl651StartIndex + 0x2);
                        } else {
                            //没有分解出有效的SL651协议报文，位置索引加1，下个字节开始再判断其他协议
                            index++;
                        }
                    } else if (RadixConvertUtil.computeIntValue(packetsBytes[index]) == this.Frame_Start_Bytes_Custom1[0x0] &&
                            RadixConvertUtil.computeIntValue(packetsBytes[index + 1]) == this.Frame_Start_Bytes_Custom1[0x1]) {
                        //需要将byte转换为int进行比较，byte表示的范围不能大于0x7F
                        //匹配第3种：数据报文，自定义协议，起始的2字节为BEBE，报文总长度固定是39字节（包含帧起始符的2个BE）
                        if (packetsBytes.length >= index + Frame_Length_Custom1) {
                            //如果报文长度够，使用固定的报文长度39进行分隔
                            IotPacketsSplitResult splitPackets = new IotPacketsSplitResult();
                            splitPackets.setStartIndex(index);
                            splitPackets.setEndIndex(index + Frame_Length_Custom1 - 0x1);
                            byte[] bytes = Arrays.copyOfRange(packetsBytes, index, index + Frame_Length_Custom1);//copyOfRange方法中to的位置索引是不包含的
                            splitPackets.setBytes(bytes);

                            result.add(splitPackets);
                            index += Frame_Length_Custom1;
                        } else {
                            //如果报文长度不够，位置索引加1，下个字节开始再判断其他协议
                            index++;
                        }
                    } else {
                        //均不匹配，位置索引加1
                        index++;
                    }
                }

                if (result.size() > 0x0)
                    return result;
            }
        }

        return null;
    }

    /**
     * 解析数据帧
     *
     * @param inputBytes    数据帧报文字节数组，数组起始必须时数据值对应协议的起始符
     * @param parseBodyData 是否解析报文中的正文内容数据，为false时表示只解析数据帧基本结构框架，为true时表示解析解析结构框架和正文内容数据
     * @return qc.module.iot.protocol.IotFrameParseResult
     * @author QuCheng Tech
     * @since 2025/11/5
     */
    @Override
    public IotFrameParseResult parse(byte[] inputBytes, boolean parseBodyData) {
        logger.debug("parseBaseStruct");

        //初始化解析返回结果，设置解析成功标记为false
        IotFrameParseResult result = new IotFrameParseResult();
        result.setParseSuccess(false);
        result.setProtocol(protocol);
        result.setDataTime(null);//初始化数据时标为null
        //输入的字节数组赋值，根据数据协议确定是否要对Hex字符串和Ascii字符串进行赋值
        result.setInputBytes(inputBytes);
        result.setInputHexString(RadixConvertUtil.toHexString(inputBytes));

        if (inputBytes == null || inputBytes.length < this.frameMinLength)
            return returnParseError(result, "数据帧报文内容为空");
        //记录当前解析判断到的位置索引，数据帧的每个部分在解析前均要判断输入的字节数组总长是否足够
        int index = 0x0;
        //判断有效数据帧报文最小总长度
        int frameMinTotalLength = 0x0;
        //数据帧的每个结构框架部分的长度，判断到每个部分长度时进行赋值
        int framePartLength = 0x0;

        /* 德宏水文分局发送至龙江水情系统数据
         * 自定义协议，根据起始的1-2个字节不同有不同的格式
         * 第1种：心跳报文，起始的2字节为英文字母XT，无其他内容
         * 第2种：数据报文，使用SL651 Hex协议，起始的2字节为7E7E，完整使用SL651 Hex协议进行解析
         * 第3种：数据报文，自定义协议，起始的2字节为BEBE
         *
         * 第3种：数据报文，自定义协议，起始的2字节为BEBE
         * 结构框架：帧起始符 遥测站站号 未知 时间 报文正文
         * 长度字节：   2       4      2   5     2*13
         * 固定内容： 帧起始符-BEBE，报文总长度固定是39字节（包含帧起始符的2个BE）
         * 解析举例：BE BE 40 00 00 51 01 01 25 04 14 14 00 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17 11 17
         * 类型：【遥测站小时报】，数据类型：【雨量】，站号【40000051】，时间【2025-04-14 14:00:00】，值【558.5】
         * 时间格式：yyMMddHHmm，数值为对应的字符串为十进制的值*0.5分辨率 1117*0.5=558.5
         *
         */

        if (inputBytes[index] == this.Frame_Start_Bytes_Heart_Beat[0x0] && inputBytes[index + 1] == this.Frame_Start_Bytes_Heart_Beat[0x1]) {
            //匹配第1种：心跳报文，起始的2字节为英文字母XT，无其他内容
            IotPacketsDataFrameTypeEnum frameType = IotPacketsDataFrameTypeEnum.HEART_BEAT;
            result.setFrameType(frameType);
            result.setFrameName(frameType.getName());
            result.setParseSuccess(true);
            result.setParseFrameBytes(inputBytes);
            result.setParseFrameString(RadixConvertUtil.toAsciiString(inputBytes));

            return result;
        } else if (inputBytes[index] == this.Frame_Start_Bytes_SL651_Ascii[0x0] && inputBytes[index + 1] == this.Frame_Start_Bytes_SL651_Ascii[0x1]) {
            //匹配第2种：数据报文，使用SL651 Hex协议，起始的2字节为7E7E
            //调用SL651 Hex协议解析类中的解析方法
            return sl651HexFrameParse.parse(inputBytes, parseBodyData);
        } else if (RadixConvertUtil.computeIntValue(inputBytes[index]) == this.Frame_Start_Bytes_Custom1[0x0] &&
                RadixConvertUtil.computeIntValue(inputBytes[index + 1]) == this.Frame_Start_Bytes_Custom1[0x1]) {
            //需要将byte转换为int进行比较，byte表示的范围不能大于0x7F
            //匹配第3种：数据报文，自定义协议，起始的2字节为BEBE，报文总长度固定是39字节（包含帧起始符的2个BE）
            /*
             * 第3种：数据报文，自定义协议，起始的2字节为BEBE
             * 结构框架：帧起始符 遥测站站号 时间 报文正文
             * 长度字节：   2       4     5     2*13
             * 固定内容： 帧起始符-BEBE，报文总长度固定是39字节（包含帧起始符的2个BE）
             * 解析举例：BEBE 40000051 0101 2504141700 1117111711171117111711171117111711171117111711171117
             * 类型：【遥测站小时报】，数据类型：【雨量】，站号【40000051】，时间【2025-04-14 17:00:00】，值【558.5】
             * 时间格式：yyMMddHHmm，数值为对应的字符串为十进制的值*0.5分辨率 1117*0.5=558.5
             *
             */
            if (inputBytes.length < Frame_Length_Custom1)
                return returnParseError(result, "判断数据帧中的CRC校验码时报文长度不足，报文总长度应至少为" + Frame_Length_Custom1);
            //如果报文长度够，使用固定的报文长度39进行分隔
            //帧起始符，长度为2字节
            framePartLength = 0x2;
            index += framePartLength;//索引加上帧起始符的长度
            //遥测站站号，长度为4字节
            framePartLength = 0x4;
            byte[] stationAddressBytes = Arrays.copyOfRange(inputBytes, index, index + framePartLength);
            result.setStationAddressBytes(stationAddressBytes);
            result.setStationAddress(RadixConvertUtil.toHexString(stationAddressBytes));
            index += framePartLength;//索引加上遥测站站号的长度
            //未知，长度为2字节
            framePartLength = 0x2;
            index += framePartLength;//索引加上未知含义的长度
            //时间，长度为5字节
            framePartLength = 0x5;
            byte[] dataTimeBytes = Arrays.copyOfRange(inputBytes, index, index + framePartLength);
            LocalDateTime dataTime = this.parseTime(dataTimeBytes);
            if (dataTime != null) {
                result.setDataTime(dataTime);
            }
            index += framePartLength;//索引加上时间的长度
            //余下的字节全部为数据
            byte[] userDataBytes = Arrays.copyOfRange(inputBytes, index, inputBytes.length);
            String userDataHexString = RadixConvertUtil.toHexString(userDataBytes);
            //logger.debug("报文中数据Hex为：" + userDataHexString);
            result.setBodyLength(userDataBytes.length);
            result.setBodyDataBytes(userDataBytes);
            result.setBodyDataString(userDataHexString);

            //全部判断和验证均通过，解析成功；设置完整的数据帧字节数组
            IotPacketsDataFrameTypeEnum frameType = IotPacketsDataFrameTypeEnum.DATA;
            result.setFrameType(frameType);
            result.setFrameName(frameType.getName());

            result.setParseSuccess(true);
            //报文总长度固定是39字节，在分解时已经是长度为39的报文，直接使用传入的字节数组
            result.setParseFrameBytes(inputBytes);
            result.setParseFrameString(RadixConvertUtil.toAsciiString(inputBytes));

            //判断如果要解析正文中的数据进行解析
            if (parseBodyData == true && result.getBodyDataBytes() != null && result.getBodyDataBytes().length > 0x0) {
                //解析报文正文判断帧类型需要使用控制域和功能码，与解析正文方法中的顺序保持一致
                List<IotFrameBodyDataItem> bodyDataItems = new LinkedList<>();

                //每2个字节表示一个数据，数据为间隔5分钟的数据，从前1小时0分至数据时标所在小时0分共13个数据
                LocalDateTime time = dataTime.minusHours(0x1);//时间往前1小时开始
                for (int i = 0; i < 13; i++) {
                    byte[] dataValueBytes = Arrays.copyOfRange(result.getBodyDataBytes(), i * 0x2, (i + 1) * 0x2);
                    String dataValueHexString = RadixConvertUtil.toHexString(dataValueBytes);
                    int intValue = Integer.parseInt(dataValueHexString);
                    //固定使用0.5作为雨量数据分辨率，乘以分辨率
                    double doubleValue = intValue * 0.5;
                    IotFrameBodyDataItem elementDataValueItem = new IotFrameBodyDataItem();
                    elementDataValueItem.setBytes(dataValueBytes);
                    elementDataValueItem.setHexString(dataValueHexString);
                    elementDataValueItem.setType(IotFrameBodyDataItemTypeEnum.VALUE);
                    elementDataValueItem.setName("雨量");
                    elementDataValueItem.setCode("rain");
                    elementDataValueItem.setValue(doubleValue);

                    bodyDataItems.add(elementDataValueItem);

                    time = time.plusMinutes(0x5);
                }

                if (bodyDataItems.size() > 0x0)
                    result.setBodyDataItems(bodyDataItems);
            }

            logger.info("报文解析成功，正文长度[{}]，中心站地址[{}]，遥测站地址[{}]，密码[{}]，功能码[{}]，帧名称[{}]，帧类型[{}]，正文内容[{}]",
                    result.getBodyLength(), result.getCenterAddress(), result.getStationAddress(), result.getPassword(),
                    result.getFunctionCode(), result.getFrameName(), result.getFrameType(), result.getBodyDataString());

            return result;
        } else {
            return returnParseError(result, "数据帧起始符错误，无法判定使用的数据协议");
        }
    }

    /**
     * 返回数据帧解析错误结果
     *
     * @param result   返回解析结果对象
     * @param errorMsg 错误信息，返回结果中自动加上统一的前缀
     * @return qc.module.iot.protocol.IotFrameParseResult
     * @author QuCheng Tech
     * @since 2025/11/7
     */
    IotFrameParseResult returnParseError(IotFrameParseResult result, String errorMsg) {
        if (result == null) {
            result = new IotFrameParseResult();
            result.setParseSuccess(false);
        }

        result.setParseErrorMsg(parseErrorMessagePrefix + errorMsg);

        return result;
    }

    /**
     * 根据字节数组解析出时间，解析不出或者错误返回null
     *
     * @param bytes 字节数组
     * @return java.time.LocalDateTime
     * @author QuCheng Tech
     * @since 2025/11/13
     */
    LocalDateTime parseTime(byte[] bytes) {
        //传入字节数组错误或长度不够不进行解析
        if (bytes != null && bytes.length >= 0x5) {
            //5个字节表示，如十六进制字符串：25 04 14 17 00 一次表示年、月、日、时、分
            //时标中均采用BCD码，每个数均使用高4位表示十位、低4位表示个位
            int year = RadixConvertUtil.computeBcdIntValue(bytes[0x0]);
            int month = RadixConvertUtil.computeBcdIntValue(bytes[0x1]);
            int day = RadixConvertUtil.computeBcdIntValue(bytes[0x2]);
            int hour = RadixConvertUtil.computeBcdIntValue(bytes[0x3]);
            int minute = RadixConvertUtil.computeBcdIntValue(bytes[0x4]);
            int second = 0x0;

            logger.debug("parseTime " + year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second);

            LocalDateTime time = LocalDateTime.of(2000 + year, month, day, hour, minute, second);

            logger.debug("parseTime hex:" + RadixConvertUtil.toHexString(bytes) + " 解析出的时间为：" + LocalDateTimeUtil.formatDateTime(time));

            return time;
        }

        return null;
    }
}
